from Config.myConstant import *
from Config.myConfig import *
from DataAccess.TradedayDataProcess import TradedayDataProcess
from DataPrepare.tickFactorsProcess import tickFactorsProcess
from DataAccess.KLineDataProcess import KLineDataProcess
from DataPrepare.dailyFactorsProcess import dailyFactorsProcess
from Utility.JobLibUtility import JobLibUtility
from typing import List as type_list
from Strategy.baseStrategy.baseStrategy import baseStrategy
import pandas as pd
import numpy as np
import math
import xgboost as xgb
import warnings
from DataAccess.TickDataProcess import TickDataProcess
from DataPrepare.tickFactors.tickDataPrepared import tickDataPrepared
from DataAccess.IndexComponentDataProcess import IndexComponentDataProcess
from Utility.InfluxdbUtility import InfluxdbUtility
from joblib import Parallel, delayed,parallel_backend
from sklearn.metrics import r2_score
########################################################################
class CnnParameters2(baseStrategy):
    """按照预测值进行交易"""
    #----------------------------------------------------------------------
    def __init__(self):
        self.name='按照CNN的预测值进行交易'
        pass
    #----------------------------------------------------------------------
    @classmethod 
    def useJobLibToComputeByCodes(self,myfunction,stockCodes,groupnum,startDate,endDate,parameters):
        warnings.filterwarnings('ignore')
        if groupnum>len(stockCodes):
            groupnum=len(stockCodes)
            pass
        stocks={i:[] for i in range(groupnum)}
        for i in range(0,len(stockCodes)):
            mygroup=i%groupnum
            stocks[mygroup].append(stockCodes[i])
        tmpAddress={}
        with parallel_backend("multiprocessing", n_jobs=JobLibUtility.myjobs):
            mydata=Parallel()(delayed(myfunction)(list(stocks[i]),startDate,endDate,parameters) for i in range(groupnum))
        trade=[]
        statistics=[]
        for data in mydata:
            trade.append(data[0])
            statistics.append(data[1])
        trade=pd.concat(trade)
        statistics=pd.concat(statistics)
        return [trade,statistics]
    #----------------------------------------------------------------------
    def multipleCodes_parallel(self,codes:type_list[str],startDate:str,endDate:str,parameters):
        mydata=CnnParameters2.useJobLibToComputeByCodes(self.multipleCodes,codes,MYGROUPS,startDate,endDate,parameters)
        return mydata
        pass
    #----------------------------------------------------------------------
    #输入code=600000.SH，startdate=yyyyMMdd，endDate=yyyyMMdd
    def multipleCodes(self,codes:type_list[str],startDate:str,endDate:str,parameters):
        trade=[]
        statistics=[]
        for i in range(len(codes)):
            code=codes[i]
            [trade0,statistics0]=self.singleCode(code,startDate,endDate,parameters)
            trade.append(trade0)
            statistics.append(statistics0)
        trade=pd.concat(trade)
        statistics=pd.concat(statistics)
        return [trade,statistics]
  
    #----------------------------------------------------------------------
    def analysis(self,codeList,startDate,endDate,stockIndex,data):
        data=data[(data['date']>=str(startDate)) & (data['date']<=str(endDate))]

        days=list(TradedayDataProcess().getTradedays(startDate,endDate))
        days.sort()
        myWeight=IndexComponentDataProcess.getIndexProperty(stockIndex,days[0],days[0])
        weight=0
        select=[]
        for code in codeList:
            myselect=data[data['code']==code]
            if (myWeight[myWeight['code']==code]['weight'].shape[0]>0):
                weight=weight+myWeight[myWeight['code']==code]['weight'].iloc[0]
            if (myselect.shape[0]>0):
                select.append(myselect)
        
        tradeall=pd.concat(select)
        parameter2List=sorted(list(tradeall['parameter2'].unique()))
        parameter1List=sorted(list(tradeall['parameter1'].unique()))
        performance=[]
        yieldArr=np.zeros([len(parameter2List),len(parameter1List)])
        turnoverArr=np.zeros([len(parameter2List),len(parameter1List)])
        winRateArr=np.zeros([len(parameter2List),len(parameter1List)])
        holdArr=np.zeros([len(parameter2List),len(parameter1List)])
        for i in range(len(parameter2List)):
            longOpen=parameter2List[i]
            for j in range(len(parameter1List)):
                shortOpen=parameter1List[j]
                trade=tradeall[((tradeall['parameter2']==longOpen) & (tradeall['parameter1']==shortOpen))]
                if trade.shape[0]>0:
                    #grade=trade['yieldToAll'].mean()
                    #grade=(trade['yieldToAll']*trade['weight']).sum()/(trade['weight'].sum())
                    yieldArr[i,j]=round(100*((trade['yieldToAll']*trade['weight']).sum()/weight)/(len(days))*250,3)
                    turnoverArr[i,j]=round(100*(trade['turnoverRate']*trade['weight']).sum()/weight/len(days),3)
                    winRateArr[i,j]=round(100*(trade['count']*trade['winRate']).sum()/trade['count'].sum(),3)
                    holdArr[i,j]=round(trade['totalHold'].sum()/trade['count'].sum(),3)
        yieldArr=pd.DataFrame(data=yieldArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        turnoverArr=pd.DataFrame(data=turnoverArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        winRateArr=pd.DataFrame(data=winRateArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        holdArr=pd.DataFrame(data=holdArr,index=np.round(parameter2List,4),columns=np.round(parameter1List,4))
        return [yieldArr,turnoverArr,winRateArr,holdArr]
    #----------------------------------------------------------------------
    def singleCode(self,code:str,startDate:str,endDate:str,parameters):
        startDate=str(startDate)
        endDate=str(endDate)
        days=list(TradedayDataProcess().getTradedays(startDate,endDate))
        
        featureColumns=parameters['featureColumns']
        targetColumns=parameters['targetColumns']
        totalCash=parameters['totalCash']
        stockIndex=parameters['stockIndex']
        myWeight=IndexComponentDataProcess.getStockPropertyInIndex(code,stockIndex,startDate,endDate)
        predictDatabase=parameters['predictDatabase']
        transactionRatio=parameters['transactionRatio']
        maxExposureRatio=parameters['maxExposureRatio']
        maxVolumeEachTimeRatio=parameters['maxVolumeEachTimeRatio']
        parameter1=parameters['parameter1']
        parameter2=parameters['parameter2']

        tick=TickDataProcess()
        tickFactor=tickDataPrepared()
        
        newFeatureColumns=['predictMax','predictMin']
        
        statistics=[]
        trade=[]
        for day in days:
            weight=myWeight[myWeight.index==str(day)]['weight'].iloc[0]
            if np.isnan(weight)==True:
                continue
            tickData=tick.getTickShotDataFromInfluxdbServer(code,day)
            if tickData.shape[0]==0:
                continue
            preClose=tickData['dailyPreClose'].iloc[0]
            #data=tickDataPrepared.getTickFactorDataFromInfluxdb(code,day,columns=featureColumns)
            #从本地文件夹读写数据
            #path=os.path.join(LocalFileAddress,'strategyCnnData20191109',day)
            path=os.path.join(r'C:/strategyCnnData20191109',day)
            HDF5Utility.pathCreate(path)
            fileName=os.path.join(path,code+'.h5')
            h5Data=super().getDataFromH5(fileName)
            if h5Data.shape[0]==0:
                data=InfluxdbUtility.getDataFromInfluxdb(code,day,predictDatabase,columns=featureColumns+targetColumns)
                if data.shape[0]==0:
                    continue
                data[newFeatureColumns]=data[featureColumns]
                tickData=pd.merge(tickData,data,how='left',left_index=True,right_index=True)
                super().saveDataToH5(tickData,fileName)
            else:
                tickData=h5Data
            
            tmpData=tickData[targetColumns+newFeatureColumns].dropna(axis=0, how='any')
            r2_1=r2_score(tmpData[targetColumns[0]],tmpData[newFeatureColumns[0]])
            r2_2=r2_score(tmpData[targetColumns[1]],tmpData[newFeatureColumns[1]])
            corr_1=tmpData[[targetColumns[0],newFeatureColumns[0]]].corr().iloc[0,1]
            corr_2=tmpData[[targetColumns[1],newFeatureColumns[1]]].corr().iloc[0,1]
            del(tmpData)
            maxPosition=round(totalCash*(weight/100)/preClose,-2)
            maxExposure=round(totalCash*(weight/100)*maxExposureRatio/preClose,-2)
            maxVolumeEachTime=max(round(totalCash*(weight/100)*maxVolumeEachTimeRatio/preClose,-2),100)
            if maxPosition<100:
                continue
            for col in featureColumns:
                if (col in list(tickData.columns))==False:
                    continue
                if tickData[col].isna().sum()==tickData.shape[0]:
                    continue
            for i in parameter1:
                for j in parameter2:
                    parameters={'code':code,'date':day,'maxPosition':maxPosition,'maxExposure':maxExposure,'maxVolumeEachTime':maxVolumeEachTime,'parameter1':i,'parameter2':j,'transactionRatio':transactionRatio}    
                    [trade0,statistics0]=self.strategy(tickData,parameters)
                    if statistics0.shape[0]>0:
                        statistics0['weight']=weight
                        statistics0['parameter1']=i
                        statistics0['parameter2']=j
                        statistics0['r2_1']=r2_1
                        statistics0['r2_2']=r2_2
                        statistics0['corr_1']=corr_1
                        statistics0['corr_2']=corr_2
                        trade0['parameter1']=i
                        trade0['parameter2']=j
                        statistics.append(statistics0)
                        trade.append(trade0)
            pass
        if len(statistics)==0:
            statistics=pd.DataFrame()
            trade=pd.DataFrame()
        else:
            statistics=pd.concat(statistics)
            
            trade=pd.concat(trade)
        logger.info(f'backtest of {code} complete!!')
        return [trade,statistics]
     #----------------------------------------------------------------------
    def strategy(self,data:pd.DataFrame,parameters):
        
        maxPosition=parameters['maxPosition']
        maxExposure=parameters['maxExposure']
        para1=parameters['parameter1']
        para2=parameters['parameter2']
        transactionRatio=parameters['transactionRatio']
        maxVolumeEachTime=parameters['maxVolumeEachTime']
        code=parameters['code']
        date=parameters['date']
        openPara=para1
        multiplier=para2
        unusedPosition=maxPosition
        todayPosition=0
        myindex={}
        select=list(data.columns)
        for item in data.columns:
            myindex.update({item:select.index(item)})
        mydata=data.values
        openPrice=0
        openAmount=0
        openVolume=0
        maxPrice=0
        minPrice=9999
        stop=False
        longSignal=False
        longCloseSignal=False
        shortSignal=False
        shortCloseSignal=False
        maxDownDraw=0
        openDownDraw=0
        openIndex=-1
        holdTime=0
        transactionIndex=0
        trade=[]
        for i in range(len(mydata)):
            tick=mydata[i] 
            maxPredict=tick[myindex['predictMax']]/100.0
            minPredict=tick[myindex['predictMin']]/100.0
            realData=tick[myindex['realData']]
            S1=tick[myindex['S1']]
            B1=tick[myindex['B1']]
            mid=(S1+B1)/2
            SV1=tick[myindex['SV1']]
            BV1=tick[myindex['BV1']]
            time=tick[myindex['tick']]
            date=tick[myindex['date']]
            if mid>0:
                spread=abs(S1-B1)/mid
            else:
                spread=0
            limitMultiplier=1
            if (maxPredict>openPara) & (multiplier*maxPredict>-(minPredict-2*spread)):
                longSignal=True
                #limitMultiplier=abs(maxPredict/(minPredict-2*spread))
                #limitMultiplier=max(1/multiplier,5)
                if ((minPredict-2*spread)<0):
                    limitMultiplier=-maxPredict/(minPredict-2*spread)
                    limitMultiplier=min(limitMultiplier,5)
                else:
                    limitMultiplier=5
                
            else:
                longSignal=False
            if (minPredict<-openPara)  & (multiplier*minPredict<-(maxPredict+2*spread)):
                shortSignal=True
                #limitMultiplier=abs(minPredict/(maxPredict+2*spread))
                #limitMultiplier=max(1/multiplier,5)
                if (maxPredict+2*spread)>0:
                    limitMultiplier=-minPredict/(maxPredict+2*spread)
                    limitMultiplier=min(limitMultiplier,5)
                else:
                    limitMultiplier=5
            else:
                shortSignal=False
            if (todayPosition>0) &  (minPredict<-openPara/3):
                longCloseSignal=True
                #limitMultiplier=1
            else:
                longCloseSignal=False
            if (todayPosition<0)  & (maxPredict>openPara/3):
                shortCloseSignal=True
                #limitMultiplier=1
            else:
                shortCloseSignal=False
            maxVolumeEachTime=round(parameters['maxVolumeEachTime']*limitMultiplier,-2)
            maxExposure=parameters['maxExposure']+maxVolumeEachTime-parameters['maxVolumeEachTime']
            increaseToday=tick[myindex['midPrice']]/tick[myindex['dailyPreClose']]-1
            #遇上涨跌停直接不做
            if (abs(increaseToday)>=0.095) & (todayPosition==0):
                break
            #止盈止损
            if todayPosition>0:
                if mid>maxPrice:
                    maxPrice=mid
                openVwapPrice=openAmount/openVolume
                maxDownDraw=min(0,(mid-maxPrice)/openVwapPrice)
                openDownDraw=min(0,(mid-openPrice)/openPrice)
                openYield=(mid-openPrice)/openPrice
                openVwapDownDraw=min(0,(mid-openVwapPrice)/openVwapPrice)
                if openIndex>0:
                    holdTime=i-openIndex
                if (openDownDraw<=-0.01):
                    stop=True
                else:
                    stop=False
                pass
            elif todayPosition<0:
                if mid<minPrice:
                    minPrice=mid
                openVwapPrice=openAmount/openVolume
                maxDownDraw=min(0,(minPrice-mid)/openVwapPrice)
                openDownDraw=min(0,(openPrice-mid)/openPrice)
                openYield=(mid-openPrice)/openPrice
                openVwapDownDraw=min(0,(openVwapPrice-mid)/openVwapPrice)
                if openIndex>0:
                    holdTime=i-openIndex
                if (openDownDraw<=-0.01):
                    stop=True
                else:
                    stop=False
                pass
            if todayPosition!=0:
                if todayPosition>0:#持有多仓
                    myclose=False
                    if  (time>='145500000') | (increaseToday<-0.095):
                        [averagePrice,sellPosition]=super().sellByTickShotData(tick,myindex,abs(todayPosition),0.05)
                        transactionVolume=sellPosition
                        todayPosition=todayPosition-transactionVolume
                        transactionPrice=averagePrice
                        transactionTime=time
                        transactionDate=date
                        myclose=True
                        pass
                    elif ((shortSignal==True) &(longSignal==False)) | (longCloseSignal==True):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*BV1,-2),abs(todayPosition))
                        if transactionVolume>0:
                            todayPosition=todayPosition-transactionVolume
                            transactionPrice=B1
                            transactionTime=time
                            transactionDate=date
                            myclose=True
                        pass
                    if  myclose==True:
                        if todayPosition!=0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'closing','transactionID':transactionIndex}
                        if todayPosition==0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'closed','transactionID':transactionIndex}
                            openPrice=0
                            maxPrice=0
                            minPrice=9999
                            maxDownDraw=0
                            openDownDraw=0
                            openIndex=-1
                            holdTime=0
                            openAmount=0
                            openVolume=0
                            stop=False
                            transactionIndex=transactionIndex+1
                        pass
                        trade.append(dict)
                    pass
                elif todayPosition<0:#持有空仓
                    myclose=False
                    if  (time>='145500000') | (increaseToday>0.095):
                        [averagePrice,buyPosition]=super().buyByTickShotData(tick,myindex,abs(todayPosition),0.05)
                        transactionVolume=buyPosition
                        todayPosition=todayPosition+transactionVolume
                        transactionPrice=averagePrice
                        transactionTime=time
                        transactionDate=date
                        myclose=True
                        pass
                    elif ((longSignal==True)&(shortSignal==False)) | (shortCloseSignal==True):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*SV1,-2),abs(todayPosition))
                        if transactionVolume>0:
                            todayPosition=todayPosition+transactionVolume
                            transactionPrice=S1
                            transactionTime=time
                            transactionDate=date
                            myclose=True
                        pass
                    if myclose==True:
                        if todayPosition!=0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'closing','transactionID':transactionIndex}
                        if todayPosition==0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'closed','transactionID':transactionIndex}
                            openPrice=0
                            maxPrice=0
                            minPrice=9999
                            maxDownDraw=0
                            openIndex=-1
                            holdTime=0
                            openAmount=0
                            openVolume=0
                            stop=False
                            transactionIndex=transactionIndex+1
                        trade.append(dict)
                    pass
            if (unusedPosition>0) & (realData==1) & (time>'093100000') & (time<'145000000'):#仍然可以开仓
                if (SV1>=100/transactionRatio) & (increaseToday>-0.09):
                    if (longSignal==True)& (shortSignal==False) &(todayPosition>=0):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*SV1,-2),unusedPosition,maxExposure-abs(todayPosition))
                        if transactionVolume>0:
                            unusedPosition=unusedPosition-transactionVolume
                            todayPosition=todayPosition+transactionVolume
                            transactionPrice=S1
                            transactionTime=time
                            transactionDate=date
                            if openIndex<0:
                                openIndex=i
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'opening','transactionID':transactionIndex}
                            trade.append(dict)
                            openAmount=openAmount+abs(transactionVolume)*transactionPrice
                            openVolume=openVolume+abs(transactionVolume)
                            if openPrice==0:
                                openPrice=transactionPrice
                                maxPrice=openPrice
                                maxDownDraw=0
                                openDownDraw=0
                            pass
                if (BV1>=100/transactionRatio)& (increaseToday<0.09):
                    if (shortSignal==True)& (longSignal==False) &(todayPosition<=0):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*BV1,-2),unusedPosition,maxExposure-abs(todayPosition))
                        if transactionVolume>0:
                            unusedPosition=unusedPosition-transactionVolume
                            todayPosition=todayPosition-transactionVolume
                            transactionPrice=B1
                            transactionTime=time
                            transactionDate=date
                            if openIndex<0:
                                openIndex=i
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'opening','transactionID':transactionIndex}
                            trade.append(dict)
                            openAmount=openAmount+abs(transactionVolume)*transactionPrice
                            openVolume=openVolume+abs(transactionVolume)
                            if openPrice==0:
                                openPrice=transactionPrice
                                minPrice=openPrice
                                maxDownDraw=0
                                openDownDraw=0
                            pass
           
        trade=pd.DataFrame(data=trade)
        if trade.shape[0]>0:
            trade['code']=code
            trade['fee']=trade['price']*0.0001
            selectBuy=trade['direction']=='buy'
            selectSell=trade['direction']=='sell'
            trade.loc[selectSell,'fee']=(trade['fee']+trade['price']*0.001)[selectSell]
            trade.loc[selectBuy,'cashChange']=((-trade['price']-trade['fee'])*trade['volume'])[selectBuy]
            trade.loc[selectSell,'cashChange']=((trade['price']-trade['fee'])*trade['volume'])[selectSell]
            trade['amount']=trade['price']*trade['volume']
            transaction=pd.DataFrame()
            transaction['profit']=trade.groupby('transactionID')['cashChange'].sum()
            transaction['volume']=trade.groupby('transactionID')['volume'].sum()/2
            transaction['amount']=trade.groupby('transactionID')['amount'].sum()/2
            transaction['yield']=round(transaction['profit']/transaction['amount'],3)
            transaction['holdTime']=round(trade.groupby('transactionID')['holdTime'].max()*0.05,3)
            turnoverRate=round(transaction['volume'].sum()/maxPosition,3)
            winRate=transaction[transaction['yield']>0].shape[0]/transaction.shape[0]
            transaction['yield']=transaction['profit']/transaction['amount']
            statistics=[{'count':transaction.shape[0],'profit':transaction['profit'].sum(),'yield':transaction['profit'].sum()/transaction['amount'].sum(),'amount':transaction['amount'].sum(),'turnoverRate':turnoverRate,'winRate':winRate,'totalHold':transaction['holdTime'].sum()}]
            statistics=pd.DataFrame(data=statistics)
            statistics['code']=code
            statistics['date']=date
            
            statistics['yieldToAll']=statistics['yield']*statistics['turnoverRate']
        else:
            statistics=pd.DataFrame()
            
        #print(trade)
        return [trade,statistics]
########################################################################